package uk.co.q3c.deplan.server.unit.service;

import java.util.Calendar;
import java.util.GregorianCalendar;

import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.Test;

import uk.co.q3c.deplan.client.domain.dag.ChangeInvalidException;
import uk.co.q3c.deplan.client.domain.dag.CycleDetectedException;
import uk.co.q3c.deplan.client.domain.resource.DefaultWorkingTime;
import uk.co.q3c.deplan.client.domain.resource.IndividualResource;
import uk.co.q3c.deplan.client.domain.resource.PersistenceReferenceException;
import uk.co.q3c.deplan.client.domain.resource.Resource;
import uk.co.q3c.deplan.client.domain.resource.ResourcePool;
import uk.co.q3c.deplan.client.domain.task.BaseTask;
import uk.co.q3c.deplan.server.dao.Model;
import uk.co.q3c.deplan.server.service.Leveller;
import uk.co.q3c.deplan.server.unit.domain.task.DomainBase_UT;

/**
 * More levelling tests
 * 
 * @author DSowerby 3 Sep 2008 23:48:42
 * 
 */
@Test
public class Leveller_Bug_Test_1 extends DomainBase_UT {
	protected final Logger logger = Logger.getLogger(getClass().getName());
	Leveller leveller;
	DefaultWorkingTime dwt = new DefaultWorkingTime();
	String testDb = "/home/dave/temp/deplan/leveller_test.db4o";
	ResourcePool resourcePool;
	// BaseCalendar baseCalendar;
	Resource resources[] = new Resource[7];

	@Override
	protected void beforeMethod() throws Exception {
		super.beforeMethod();
		// testdao = TestUtils.dbc(this, true);
		Model.setConnection(connection);
		// baseCalendar = new BaseCalendar(connection);
		buildResources();
		leveller = new Leveller(connection);
		leveller.setResourcePool(resourcePool);
		Calendar cal = new GregorianCalendar();
		cal.set(2008, 4, 5);
		leveller.setLevelDate(cal.getTime());
	}

	/**
	 * Added project but not being levelled
	 * 
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	public void bug2090933() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		BaseTask project1 = createProject("project1 ");
		// task 1
		BaseTask pt1 = new BaseTask(connection);
		project1.save();
		pt1.save();
		pt1.setParentTask(project1);
		pt1.setWorkRemaining(2000);
		pt1.assignResource(resources[0], 1);
		pt1.setPriority(100);
		// task 2
		BaseTask pt2 = new BaseTask(connection);
		pt2.save();
		pt2.setParentTask(project1);
		pt2.setWorkRemaining(2000);
		pt2.assignResource(resources[0], 1);
		pt2.setPriority(99);
		// level
		leveller.addCandidate(project1);
		leveller.level();

		// task1 should plannedFinish before task 2
		Assert.assertTrue(pt1.finish()
				.before(pt2.start()));

		// switch priorities
		pt1.setPriority(99);
		pt2.setPriority(100);
		// level again

		leveller.addCandidate(project1);
		leveller.level();

		// task2 should plannedFinish before task 1
		Assert.assertTrue(pt2.finish()
				.before(pt1.start()));

		// add another project
		BaseTask project2 = createProject("project2");
		// task 3
		BaseTask pt3 = new BaseTask(connection);
		pt3.save();
		project2.save();
		pt3.setParentTask(project2);
		pt3.setWorkRemaining(2000);
		pt3.assignResource(resources[0], 1);
		pt3.setPriority(98);
		// task 4
		BaseTask pt4 = new BaseTask(connection);
		pt4.save();
		pt4.setParentTask(project2);
		pt4.setWorkRemaining(2000);
		pt4.assignResource(resources[0], 1);
		pt4.setPriority(97);

		leveller.addCandidate(project1);
		leveller.addCandidate(project2);
		leveller.level();

		Assert.assertTrue(pt1.finish()
				.before(pt3.start()));
		Assert.assertTrue(pt2.finish()
				.before(pt3.start()));
		Assert.assertTrue(pt3.finish()
				.before(pt4.start()));

	}

	/**
	 * Task starts too early.
	 * 
	 * Task A using resource A finishes at 4pm. Task B is dependent on Task A but uses a different resource. Assuming resource B is
	 * available, Task B starts at 9.00 am, because only the day is checked
	 * 
	 * @throws CycleDetectedException
	 * @throws ChangeInvalidException
	 * @throws PersistenceReferenceException
	 */
	@Test(enabled = false)
	public void bug2095443() throws CycleDetectedException, ChangeInvalidException, PersistenceReferenceException {
		BaseTask project1 = createProject("project1 ");
		// task 1
		BaseTask pt1 = new BaseTask(connection);
		pt1.setParentTask(project1);
		// work will plannedFinish part way through first day
		pt1.setWorkRemaining(100);
		pt1.assignResource(resources[0], 1);
		pt1.setPriority(100);
		// task 2
		BaseTask pt2 = new BaseTask(connection);
		pt2.setParentTask(project1);
		pt2.setWorkRemaining(100);
		pt2.addPredecessor(pt1);
		pt2.assignResource(resources[1], 1);
		pt2.setPriority(99);
		// level
		leveller.addCandidate(project1);
		leveller.level();
		logger.info(pt1.finish());
		logger.info(pt2.start());

		// task1 should plannedFinish before task 2
		Assert.assertTrue(pt1.finish()
				.before(pt2.start()), "FAILURE EXPECTED");

	}

	private void buildResources() throws PersistenceReferenceException {
		resourcePool = new ResourcePool(connection);

		String[] names = new String[] { "dsowerby", "mpaine", "rvafadari", "ahare", "gryder", "svanage", "ldiniro" };
		for (int i = 0; i < 7; i++) {
			resources[i] = createResource(i, names[i]);
		}
	}

	private Resource createResource(int id, String name) throws PersistenceReferenceException {
		IndividualResource resource = new IndividualResource(connection);
		resource.setName(name);
		// resource.setResourceId(id);
		// resource.getResourceCalendar().setBaseCalendar(baseCalendar);
		resource.save();
		resourcePool.addResource(resource);
		return resource;
	}
}
